﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#include "stdafx.h"
#include "CommonServer.h"
#include "authorpacket.h"
#include "servicepacket.h"
#include "RoutineImp.h"

//#define __DEFAULT_ROUTINE_ID_LOAD__
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// CGateRoutine
IMPLEMENT_RTTI_CREATE( CGateRoutine, CObject, CObjectTraits::OBJECTT_NONE )

UInt CGateRoutine::OnHandle(Int nEvent, uintptr_t utData, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_TCP_CLOSED:
        {
            // 还在业务区流程验证的, 关闭导致授权状态检测超时重新开始业务区流程, 不需要通知
            // 已经业务区流程验证/完成, 网络层属性设置为 PAKT_EXTERNAL_MASTER + Type
            // !!!!!!没有和任务业务功能连接的不需要通知业务管理!!!!!!
            CGateSessionPtr SessionPtr;
            if (RemoveSession(ullParam, utData, SessionPtr))
            {
                // 1. 没有和任务业务功能连接的不需要通知业务管理
                CGateSession::ARY_ROUTINEID aRID;
                if (SessionPtr->Logout(aRID) && (m_uMasterId != 0))
                {
#ifdef __DUMP_STEP_DETAIL__
                    DEV_DUMP(TF("[业务网关例程]外网端断开[%p] --- 连接的业务有%d, 由业务管理转发外网端断开给对应业务!"), ullParam, aRID.GetSize());
#endif
                    CNETSrvLogout Logout;
                    Logout.SetType(PAKT_GATE);
                    Logout.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_GATE)->usId);
                    Logout.SetSessionId(ullParam);

                    CStream& Stream = Logout.Alloc(m_pServer->GetNetworkPtr());
                    Stream << (UInt)aRID.GetSize();
                    Stream.Write(aRID.GetData(), sizeof(ULLong) * aRID.GetSize());
                    Logout.Send(m_pServer->GetSocket(PAKT_MASTER), m_pServer->GetNetworkPtr());
                }
#ifdef __DUMP_STEP_DETAIL__
                else
                {
                    DEV_DUMP(TF("[业务网关例程]外网端断开[%p] --- 没有连接的业务%d或者和业务管理连接已经断开!"), ullParam, aRID.GetSize());
                }
#endif
                // 2. 授权连接还存在
                if (utData != 0)
                {
#ifdef __DUMP_STEP_DETAIL__
                    DEV_DUMP(TF("[业务网关例程]外网端断开[%p] --- 授权连接可能在线, 通知授权连接[%d]尝试重新开始业务区签证流程!"), ullParam, SessionPtr->GetRet());
#endif
                    CPAKCompleteAck Ack((UACK)SessionPtr->GetRet());
                    Ack.SetSessionId(utData);
                    m_pServer->GetNetworkPtr()->SendTo(m_pServer->GetSocket(PAKT_AUTHOR), Ack, SessionPtr->GetAddr());
                }
#ifdef __DUMP_STEP_DETAIL__
                else
                {
                    DEV_DUMP(TF("[业务网关例程]外网端断开[%p] --- 授权连接已经断开, 需要重新授权登陆!"), ullParam);
                }
#endif
            }
#ifdef __DUMP_STEP_DETAIL__
            else
            {
                DEV_DUMP(TF("[业务网关例程]外网端断开[%p] --- 外网端业务区签证流程未完成, 授权超时重新开始业务区签证流程!"), ullParam);
            }
#endif
            m_pServer->GetServerInfo(PAKT_GATE)->DecrOnline();
        }
        break;
    case PAKE_LINKACK:
        {
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务网关例程]业务管理连接[id=%d]成功, 通知所有外网端就绪, 由业务管理转发让在线业务发送在线消息给所有外网端!"), utData);
            m_pServer->GetServerInfo(PAKT_GATE)->Clear();
            m_pServer->GetMgrHandler()->OnHandle(PAKE_SYNC, utData, PAKT_MASTER);
            CAtomics::Exchange<UInt>(&m_uMasterId, (UInt)utData);
            // 通知外网端网关和业务管理连接完成, 等待在线业务服务器通知
            // ROUTINE_ERROR_REQ_MASTER
            CPAKSrvLinkAck Gate((UACK)ROUTINE_ERROR_REQ_MASTER);
            Gate.SetType(PAKT_GATE);
            m_pServer->GetNetworkPtr()->Send(0, Gate, CNETTraits::SEND_BROADCAST_GTOE, PAKT_EXTERNAL_MASTER);
            // 通知业务管理让在线业务功能发送在线情况
            ResetAck(&Gate, PAKE_SERVICE_LOGIN);
            Gate.SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_GATE)->usId);
            Gate.SetSessionId(ullParam);
            m_pServer->GetNetworkPtr()->Send(m_pServer->GetSocket(PAKT_MASTER), Gate);
        }
        break;
    case PAKE_UNLINKACK:
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务网关例程]业务管理连接[id=%d]断开, 通知所有外网端异常, 删除所有在线业务信息, 删除每个会话保存的与业务功能连接的记录!"), m_uMasterId);
            m_pServer->GetMgrHandler()->OnHandle(PAKE_SYNC, 0, PAKT_MASTER);
            CAtomics::Exchange<UInt>(&m_uMasterId, 0);
            // 如果有连接存在, 需要通知外网端网关和业务管理断开, 业务请求暂停
            // 外网端有请求回复同样的通知
            // ROUTINE_ERROR_REQ_MASTER
            CPAKSrvUnlinkAck Ack(ROUTINE_ERROR_REQ_MASTER);
            Ack.SetType(PAKT_GATE);
            m_pServer->GetNetworkPtr()->Send(0, Ack, CNETTraits::SEND_BROADCAST_GTOE, PAKT_EXTERNAL_MASTER);
            // 删除所有在线业务RID
            RemoveAllRoutineId();
            // 删除每个会话保存的与业务功能连接的记录
            UnlinkSessionRId(0);
        }
        break;
    default: {}
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, utData, ullParam);
    }
    return RET_OKAY;
}

UInt CGateRoutine::OnHandle(Int nEvent, CEventBase& EventRef, ULLong ullParam)
{
    switch (nEvent)
    {
    case PAKE_AUTHOR_VISA:   // author
        {
            OnMasterVisa(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_BOND:  // 外网端
        {
            OnExternalBond(EventRef, ullParam);
        }
        break;
    case PAKE_SERVICE_LOGIN: // 外网端
        {
            OnExternalLogin(EventRef, ullParam);
        }
        break;
    // 业务在线/上线消息, 记录在在线列表, 广播给所有外网端
    case PAKE_SERVICE_LINKACK:
        {
            //ROUTINE_ERROR_LINK_ID上线, session-id, srv-id
            //ROUTINE_ERROR_LINK_ONLINE在线, session-id, srv-id
            CNETSrvLinkAck* pAck = static_cast<CNETSrvLinkAck*>(&EventRef);
            pAck->SetType(PAKT_GATE);

            AddRoutineId(pAck->GetSessionId());
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务网关例程]业务功能服务器在线/上线消息[%d], 通知所有外网端就绪!"), pAck->GetAck());
            pAck->BroadcastSend(m_pServer->GetNetworkPtr(), CNETTraits::SEND_BROADCAST_GTOE, PAKT_EXTERNAL_MASTER);
        }
        break;
    // 业务断线/停用(ACK=ROUTINE_ERROR_LINK_ONLINE/ROUTINE_ERROR_LINK_DISABLED)消息, 在线列表删除, 广播给所有外网端
    case PAKE_SERVICE_UNLINKACK:
        {
            //ROUTINE_ERROR_LINK_ONLINE断线, 数据流为RID列表, srv-id
            //ROUTINE_ERROR_LINK_DISABLED停用, session-id, srv-id
            //ROUTINE_ERROR_REQ_SERVICE不在线, session-id-------------OnRoutine
            CNETSrvUnlinkAck* pAck = static_cast<CNETSrvUnlinkAck*>(&EventRef);
            pAck->SetType(PAKT_GATE);

            if (pAck->GetAck() != ROUTINE_ERROR_LINK_ONLINE)
            {
                RemoveRoutineId(pAck->GetSessionId());
            }
            else
            {
                CStream& Stream = pAck->GetStream();

                UInt uCount = 0;
                Stream >> uCount;
                for (UInt i = 0; i < uCount; ++i)
                {
                    ULLong ullId = 0;
                    Stream >> ullId;
                    RemoveRoutineId(ullId);
                }
            }
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务网关例程]业务功能服务器断线/停用消息[%d], 通知所有外网端等待!"), pAck->GetAck());
            pAck->ResetBroadcastSend(m_pServer->GetNetworkPtr(), pAck->Length(), CNETTraits::SEND_BROADCAST_GTOE, PAKT_EXTERNAL_MASTER);
        }
        break;
    case PAKE_SERVICE_LOGOUTACK: // 踢除特定外网端
            {
                CNETSrvLogoutAck* pAck = static_cast<CNETSrvLogoutAck*>(&EventRef);
                pAck->SetType((UTYPE)PAKT_GATE);
                ullParam = pAck->GetSessionId();

                CGateSessionPtr SessionPtr;
                if (GetSession(ullParam, SessionPtr))
                {
                    SessionPtr->SetRet(ROUTINE_ERROR_KICK_LOGOUT);
                }
                if (m_pServer->GetNetworkPtr()->GetAttr((Socket_t)ullParam) >= PAKT_EXTERNAL_MASTER)
                {
                    pAck->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr(), true);
                }
            }
            break;
    case PAKE_SERVICE_BROADCASTACK: // 广播
        {
            CNETSrvBroadcastAck* pAck = static_cast<CNETSrvBroadcastAck*>(&EventRef);
            Int    nFlag       = (Int)pAck->GetSrvId();
            ULLong ullLiveData = pAck->GetSessionId();

            DEV_DUMP(TF("[业务网关例程]收到广播数据包Flag=%d, Live-Data=%llX, size=%d"), pAck->GetSrvId(), pAck->GetSessionId(), pAck->Length());
            LOGV_DUMP(m_pServer->GetFileLog(), TF("[业务网关例程]收到广播数据包Flag=%d, Live-Data=%llX, size=%d"), pAck->GetSrvId(), pAck->GetSessionId(), pAck->Length());

            pAck->SetEvent((UEVENT)PAKE_ROUTINE_LIVEACK);
            pAck->SetType((UTYPE)PAKT_GATE);
            pAck->SetSrvId(0);
            pAck->SetSessionId(0);

            pAck->ResetBroadcastSend(m_pServer->GetNetworkPtr(), pAck->Length(), nFlag, ullLiveData);
        }
        break;
    case PAKE_SERVICE_MAINTAINACK:
        {
            // 广播给网关 时间有效并且和原来的不一样
        }
        break;
    default:
        {
            if ((nEvent >= PAKE_ROUTINE_LINK) && (nEvent < PAKE_ROUTINE_LINKACK)) // 外网端
            {
                OnRoutine(EventRef, ullParam);
            }
            else if ((nEvent >= PAKE_ROUTINE_LINKACK) && (nEvent <= PAKE_ROUTINE_FAIL))
            {
                OnRoutineAck(EventRef, ullParam);
            }
        }
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, EventRef, ullParam);
    }
    return RET_OKAY;
}

UInt CGateRoutine::OnHandle(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->OnHandle(nEvent, Stream, ullParam);
    }
    return RET_OKAY;
}

UInt CGateRoutine::OnHandle(Int, UInt)
{
    CheckQueueSessionStatus();
    if (m_bCompleteLive == false)
    {
        CheckConfirmSessionStatus();
    }
    return RET_OKAY;
}

bool CGateRoutine::Start(ICommonServer* pServer)
{
    m_pServer = pServer;
    if ((m_pServer != nullptr) && LoadExtConfig())
    {
        HASH_DUMP dump;
        dump.llUsedCount = 0xEEEEEEEE;
        dump.llUsedTick  = sizeof(ULLong);
        CHash::Dump(dump);

        m_pKeyPtr = reinterpret_cast<PULLong>(dump.llUsedCount);

        // 默认必须创建异步事件队列
        m_uThreads = (UInt)m_pServer->GetConfig().GetServerValue(CServerConfig::CFG_LOAD_GATE);
        m_uThreads = CPlatform::CheckThread(m_uThreads);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]异步事件队列处理线程=%d, %s"), m_uThreads, (m_uThreads > 1) ? TF("启用多线程并发处理") : TF("启用单线程处理"));
        m_nUpdate         = 0;
        // 超时为更新周期的倍数
        SetTimeout();

        m_RIdInfo.Init();

        m_QueueMap.SetHash(MAP_HASH_MIN);
        m_ConfirmMap.SetHash(MAP_HASH_MIN);
        m_SessionMap.SetHash(MAP_HASH_MIN);
        
        CEventQueue::CreateTickEvent(SESSION_ID_GATE, m_uTimeoutTick, *this);

        if (m_BusinessPtr != nullptr)
        {
            if (m_BusinessPtr->Start(pServer) == false) // 注意异步事件队列还未初始化
            {
                return false;
            }
        }
        if (CEventQueue::EventQueue(m_EventQueuePtr, *this, 0, (Int)m_uThreads))
        {
            m_EventQueuePtr->SetThreadEvent(PAKE_THREAD_START, PAKE_THREAD_STOP);
            return m_EventQueuePtr->Init();
        }
        return true;
    }
    return false;
}

bool CGateRoutine::Pause(bool bPause)
{
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Pause(bPause);
    }
    return false;
}

bool CGateRoutine::Update(void)
{
    if (m_pServer->GetServerInfo(PAKT_GATE)->IsMayDay() == false)
    {
        if (m_EventQueuePtr->CheckThreads(m_uTimeout))
        {
            m_pServer->GetServerInfo(PAKT_GATE)->MayDay();
            DEV_ERROR(TF("[业务网关例程]事件队列线程异常, 进入异常状态"));
            LOG_ERROR(m_pServer->GetFileLog(), TF("[业务网关例程]事件队列线程异常, 进入异常状态"));
        }
    }
    if (CEventQueue::CheckTickEvent(SESSION_ID_GATE, DATAD_TIME))
    {
        DEV_WARN(TF("[业务网关例程]超时检测线程异常, 重启定时器"));
        LOG_WARN(m_pServer->GetFileLog(), TF("[授权网关]超时检测线程异常, 重启定时器"));
        CEventQueue::DestroyTickEvent(SESSION_ID_GATE);
        CEventQueue::CreateTickEvent(SESSION_ID_GATE, m_uTimeoutTick, *this);
    }
    // 定时更新线程不需要检测
    // 定时更新线程异常导致不向同步管理发送心跳包, 会导致本服务器离线

    // 定时检测扩展配置文件是否有修改
    ++m_nUpdate;
    if (m_nUpdate >= m_nTime)
    {
        m_nUpdate = 0;
        LoadExtConfig(true);

        RID_INFO_MAP RIdInfo = m_RIdInfo.GetCurr();
        if (RIdInfo.GetSize() > 0)
        {
            CGateSession::ARY_ROUTINEID aRID;
            Int nCount = 0;
            for (PINDEX index = RIdInfo.GetFirstIndex(); index != nullptr; )
            {
                RID_INFO_MAP_PAIR* pPair = RIdInfo.GetNext(index);
                if ((pPair->m_V.uUpdate == TRUE) && (pPair->m_V.llReqCount > 0))
                {
                    aRID.Add(pPair->m_K);
                    aRID.Add(pPair->m_V.llReqCount);
                    aRID.Add(pPair->m_V.llAckCount);
                    ++nCount;
                }
            }
            if (nCount > 0)
            {
                m_pServer->GetMgrHandler()->OnHandle(PAKE_UPDATE, reinterpret_cast<uintptr_t>(aRID.GetData()), (ULLong)nCount);
            }
        }
    }
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Update();
    }
    return true;
}

void CGateRoutine::Stop(void)
{
    CEventQueue::DestroyTickEvent(SESSION_ID_GATE);
    if (m_BusinessPtr != nullptr)
    {
        m_BusinessPtr->Stop();
        CString strName = m_BusinessPtr->GetRTTI().GetName();
        m_BusinessPtr->Exit();
        m_BusinessPtr = nullptr;
        CObjectTraits::Unload<ICommonRoutine>(*strName);
    }
    if (m_EventQueuePtr != nullptr)
    {
        m_EventQueuePtr->Exit();
        m_EventQueuePtr = nullptr;
    }
    m_RIdInfo.Exit();

    m_QueueMap.RemoveAll();
    m_ConfirmMap.RemoveAll();
    m_SessionMap.RemoveAll();

    m_pServer         = nullptr;
    m_pKeyPtr         = nullptr;
    m_nUpdate         = 0;
    m_uThreads        = 0;
    m_uTimeout        = 0;
    m_uMasterId       = 0;

    m_uTimeoutTick    = SESSION_TIME_OUT_TICK;
    m_uTimeoutMin     = SESSION_TIME_OUT_MIN;
    m_uTimeoutMax     = SESSION_TIME_OUT_MAX;

    m_bCompleteLive   = false;
    m_bRID2External   = false;
}

bool CGateRoutine::SetTimeout(UInt uTimeout)
{
    if (uTimeout == 0)
    {
        m_uTimeout = m_pServer->GetConfig().GetUpdateTick() * DATAD_TIME;
    }
    else
    {
        m_uTimeout = uTimeout;
        m_uTimeout = DEF::Max<UInt>(1, m_uTimeout);
    }
    return true;
}

bool CGateRoutine::AddQueue(Int nEvent, uintptr_t utData, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "ServerAck"
    // PAKE_LINKACK/PAKE_UNLINKACK, id, master
    // 外网端断开
    // PAKE_TCP_CLOSED, live-data, socket
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, utData, ullParam, eType);
    }
    switch (nEvent)
    {
    case PAKE_LINKACK:    // 向管理服务器注册回复结果
    case PAKE_UNLINK:     // 向本服务器注册的注销/断开
    case PAKE_UNLINKACK:  // 向管理服务器注销/断开回复结果
    case PAKE_TCP_CLOSED: // 连接的外网端注销/断开
        {
            bRet = true;
        }
        break;
    default: {}
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, utData, ullParam, eType);
    }
    return bRet;
}

bool CGateRoutine::AddQueue(Int nEvent, CEventBase& EventRef, ULLong ullParam, QUEUE_TYPE eType)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "CommonEvent"
    // [PAKE_COMMON_BEGIN, PAKE_COMMON_END], EventRef, PTCP_PARAM/PUDP_PARAM
    //
    // [PAKE_ROUTINE_BEGIN, PAKE_ROUTINE_FAIL], EventRef, PTCP_PARAM
    // [PAKE_SERVICE_BEGIN, PAKE_SERVICE_END], EventRef, PTCP_PARAM
    // [PAKE_AUTHOR_BEGIN, PAKE_AUTHOR_ACK), EventRef, PUDP_PARAM
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, EventRef, ullParam, eType);
    }

    ULLong ullSocket = ullParam;
    if (eType > QUEUE_TYPE_MASK)
    {
        CNETTraits::PTCP_PARAM pTcp = reinterpret_cast<CNETTraits::PTCP_PARAM>(ullParam);
        ullSocket = pTcp->sSocket;
    }
    if ((nEvent >= PAKE_ROUTINE_BEGIN) && (nEvent <= PAKE_ROUTINE_FAIL))
    {
        bRet = true;
        (static_cast<CNETRoutine*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    else if ((nEvent >= PAKE_SERVICE_BEGIN) && (nEvent <= PAKE_SERVICE_END))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    // Author
    else if ((nEvent >= PAKE_AUTHOR_BEGIN) && (nEvent < PAKE_AUTHOR_ACK))
    {
        bRet = true;
        (static_cast<CNETSession*>(&EventRef))->Attach(ullParam, eType, m_pServer->GetNetworkPtr());
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, EventRef, ullSocket, (eType & QUEUE_TYPE_MASK));
    }
    return bRet;
}

bool CGateRoutine::AddQueue(Int nEvent, CStream& Stream, ULLong ullParam)
{
    if (m_pServer == nullptr)
    {
        return false;
    }
    // 入异步事件队列前的检测尽量不要处理复杂功能, 需要处理的建议进队列回调处理
    // 有加锁的建议进队列回调处理
    // "SyncInfo"---注意流对象可能是临时实例, 不能加入队列
    // PAKE_LINK/PAKE_UPDATE, Stream, master
    bool bRet = false;
    if (m_BusinessPtr != nullptr)
    {
        bRet = m_BusinessPtr->AddQueue(nEvent, Stream, ullParam);
    }
    if (bRet)
    {
        bRet = m_EventQueuePtr->Add(nEvent, Stream, ullParam);
    }
    return bRet;
}

bool CGateRoutine::LoadExtConfig(bool bUpdate)
{
    CStringFix strPath;
    m_pServer->GetConfig().GetServerExtConfig(CServerConfig::CFG_LOAD_GATE, strPath);

    CFileAttr fa;
    m_pServer->GetConfig().GetFilePath().FileAttr(fa, *strPath);
    if (bUpdate)
    {
        if (fa.MTime(m_tExtConfig))
        {
            return true;
        }
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]定时读取文件修改时间%llX---%llX"), fa.MTime(), m_tExtConfig);
        m_tExtConfig = fa.MTime();
    }
    else
    {
        m_tExtConfig = fa.MTime();
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]初始化读取文件修改时间=%llX"), m_tExtConfig);
    }
    LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]ExtConfig=%s"), *strPath);

    CFileReadStream Reader;
    if (Reader.Create(*strPath))
    {
        CKVNode  KVNode;
        CKVStore KVStore;
        if (KVStore.Load(KVNode, Reader))
        {
            if (KVNode.Find(TF("Time")))
            {
                m_nTime = KVNode[TF("Time")].GetInt();
            }

            if (KVNode.Find(TF("TimeoutTick")))
            {
                m_uTimeoutTick = KVNode[TF("TimeoutTick")].GetUInt();
            }
            if (KVNode.Find(TF("TimeoutMin")))
            {
                m_uTimeoutMin = KVNode[TF("TimeoutMin")].GetUInt();
            }
            if (KVNode.Find(TF("TimeoutMax")))
            {
                m_uTimeoutMax = KVNode[TF("TimeoutMax")].GetUInt();
            }

            if (KVNode.Find(TF("CompleteMode")))
            {
                m_bCompleteLive = KVNode[TF("CompleteMode")].GetBoolean();
            }
            if (KVNode.Find(TF("RID2External")))
            {
                m_bRID2External = KVNode[TF("RID2External")].GetBoolean();
            }

            bool bRet = true;
            if ((bUpdate == false) && (KVNode.Find(TF("BusinessRoutine"))))
            {
                bRet = LoadExtBusiness(KVNode);
            }
            if (m_BusinessPtr != nullptr)
            {
                bRet = (m_BusinessPtr->OnHandle(bUpdate ? PAKE_CONFIG_UPDATE : PAKE_CONFIG_LOAD,
                                                reinterpret_cast<uintptr_t>(&KVNode),
                                                (ULLong)m_pServer) == RET_OKAY);
            }
            return bRet;
        }
        else
        {
            LOGV_WARN(m_pServer->GetFileLog(), TF("[业务网关例程]解析ExtConfig=%s文件内容失败"), *strPath);
        }
    }
    else
    {
        LOGV_WARN(m_pServer->GetFileLog(), TF("[业务网关例程]打开ExtConfig=%s文件不存在"), *strPath);
    }
    return true;
}

bool CGateRoutine::LoadExtBusiness(CKVNode& Root)
{
    CKVString strName;
    CKVString strObject;
    CKVString strModule;

    CKVNode& Routine = Root[TF("BusinessRoutine")];
    Routine[TF("Name")].GetString(strName);
    Routine[TF("Object")].GetString(strObject);
    Routine[TF("Module")].GetString(strModule);
#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_LINUX)
    strModule.Insert(0, CServerConfig::LibPrefix);
#endif
    strModule += CServerConfig::LibExt;

    assert(m_BusinessPtr == nullptr);
    if (CObjectTraits::Load<ICommonRoutine>(*strName, *strModule, m_BusinessPtr, *strObject))
    {
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]RTTI load --- Object=%s, Name=%s, Module=%s --- %p"), *strObject, *strName, *strModule, m_BusinessPtr.Get());
        if (m_BusinessPtr->Init() == (UInt)RET_OKAY)
        {
            m_BusinessPtr->OnHandle(PAKE_CONFIG_DEFAULT, reinterpret_cast<uintptr_t>(this), (ULLong)m_pServer);
            return true;
        }
    }
    LOGV_ERROR(m_pServer->GetFileLog(), TF("[业务网关例程]RTTI load --- Object=%s, Name=%s, Module=%s --- Failed!"), *strObject, *strName, *strModule);
    return false;
}

bool CGateRoutine::AddRoutineId(ULLong ullRoutineId)
{
    CSyncLockWaitScope scope(m_RIdInfo.Lock);
    RID_INFO_MAP& RIdInfo = m_RIdInfo.GetNextClone();

    RID_INFO_MAP_PAIR* pPair = RIdInfo.Find(ullRoutineId);
    if (pPair == nullptr)
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]业务RID=%llX在线!"), ullRoutineId);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]业务RID=%llX在线!"), ullRoutineId);
#endif
        ROUTINEID_INFO ri;
        ri.uStatus    = STATUSL_LINKED;
        ri.uUpdate    = FALSE;
        ri.llReqCount = 0;
        ri.llAckCount = 0;
        RIdInfo.Add(ullRoutineId, ri);
        m_RIdInfo.Swap();

        m_pServer->GetMgrHandler()->OnHandle(PAKE_LINK, ullRoutineId, PAKT_SERVICE);
        return true;
    }
    else if (pPair->m_V.uStatus == STATUSL_RESERVED)
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]业务RID=%llX上线!"), ullRoutineId);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]业务RID=%llX在线!"), ullRoutineId);
#endif
        pPair->m_V.uStatus = STATUSL_LINKED;
        m_RIdInfo.Swap();

        m_pServer->GetMgrHandler()->OnHandle(PAKE_LINK, ullRoutineId, PAKT_SERVICE);
        return true;
    }
    return false;
}

bool CGateRoutine::RemoveRoutineId(ULLong ullRoutineId)
{
    CSyncLockWaitScope scope(m_RIdInfo.Lock);
    RID_INFO_MAP& RIdInfo = m_RIdInfo.GetNextClone();

    RID_INFO_MAP_PAIR* pPair = RIdInfo.Find(ullRoutineId);
    if (pPair != nullptr)
    {
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]业务RID=%llX断线/停用!"), ullRoutineId);
        LOGV_INFO(m_pServer->GetFileLog(), TF("[业务网关例程]业务RID=%llX断线/停用!"), ullRoutineId);
#endif
        pPair->m_V.llReqCount = 0;
        pPair->m_V.llAckCount = 0;
        pPair->m_V.uUpdate    = FALSE;
        pPair->m_V.uStatus    = STATUSL_RESERVED;
        m_RIdInfo.Swap();

        m_pServer->GetMgrHandler()->OnHandle(PAKE_UNLINK, ullRoutineId, PAKT_SERVICE);
        return true;
    }
    return false;
}

bool CGateRoutine::CheckRoutineId(ULLong ullRoutineId)
{
    RID_INFO_MAP_PAIR* pPair = m_RIdInfo.GetCurr().Find(ullRoutineId);
    if (pPair != nullptr)
    {
        return (pPair->m_V.uStatus == STATUSL_LINKED);
    }
    return false;
}

bool CGateRoutine::LinkRoutineId(ULLong ullRoutineId)
{
    RID_INFO_MAP_PAIR* pPair = m_RIdInfo.GetCurr().Find(ullRoutineId);
    if (pPair != nullptr)
    {
        pPair->m_V.uUpdate = TRUE;
        CAtomics::Increment64<LLong>(&pPair->m_V.llReqCount);
        return true;
    }
    return false;
}

bool CGateRoutine::LinkAckRoutineId(ULLong ullRoutineId)
{
    RID_INFO_MAP_PAIR* pPair = m_RIdInfo.GetCurr().Find(ullRoutineId);
    if (pPair != nullptr)
    {
        pPair->m_V.uUpdate = TRUE;
        CAtomics::Increment64<LLong>(&pPair->m_V.llAckCount);
        return true;
    }
    return false;
}

bool CGateRoutine::GetAllRoutineId(CGateSession::ARY_ROUTINEID& aRID)
{
    CSyncLockWaitScope scope(m_RIdInfo.Lock);
    RID_INFO_MAP& RIdInfo = m_RIdInfo.GetCurr();
    if (RIdInfo.GetSize() > 0)
    {
        Int nCount = 0;
        for (PINDEX index = RIdInfo.GetFirstIndex(); index != nullptr; )
        {
            RID_INFO_MAP_PAIR* pPair = RIdInfo.GetNext(index);
            if (pPair->m_V.uStatus <= STATUSL_LINKED)
            {
                aRID.Add(pPair->m_K);
                ++nCount;
            }
        }
        return (nCount > 0);
    }
    return false;
}

bool CGateRoutine::RemoveAllRoutineId(void)
{
    CSyncLockWaitScope scope(m_RIdInfo.Lock);
    RID_INFO_MAP& RIdInfo = m_RIdInfo.GetNextClone();
#ifdef __DUMP_STEP_DETAIL__
    DEV_DUMP(TF("[业务网关例程]所有业务RID=%d删除!"), RIdInfo.GetSize());
#endif
    RIdInfo.RemoveAll();
    m_RIdInfo.Swap();

    m_pServer->GetMgrHandler()->OnHandle(PAKE_UNLINK, 0, PAKT_SERVICE);
    return true;
}

bool CGateRoutine::OnMasterVisa(CEventBase& EventRef, ULLong ullParam)
{
    CNETVisa* pVisa = static_cast<CNETVisa*>(&EventRef);

    ullParam = pVisa->GetSessionId();
    // NET_ADDR + SESSION_ID + DATA
    CStream& Stream = pVisa->GetStream();
    CNetAddr NetAddr;
    NetAddr.Serialize(Stream);

    SESSION_ID sid;
    Stream.Read(&sid, sizeof(SESSION_ID));

#ifdef __DUMP_STEP_DETAIL__
    DEV_DUMP(TF("[业务网关例程]授权连接[%p]业务区签证流程申请开始, 读取申请数据[授权地址+SESSION_ID+业务数据]!"), ullParam);
#endif

    UACK uAck = (UACK)ERRORD_OKAY;

    if (m_uMasterId == 0)
    {
        uAck = (UACK)ROUTINE_ERROR_REQ_MASTER;
    }
    else if (m_pServer->GetServerInfo(PAKT_GATE)->IsFullLoad(false) == false)
    {
        CSyncLockWaitScope scope(m_QueueLock);
        CGateSessionPtr SessionPtr;
        GATE_SESSION_MAP_PAIR* pPair = m_QueueMap.Find(ullParam);
        if (pPair == nullptr)
        {
            SessionPtr = MNEW CGateSession;
            m_QueueMap.Add(ullParam, SessionPtr);
        }
        else if (pPair->m_V->GetStatus() == SESSION_STATUS_VISA)
        {
            SessionPtr = pPair->m_V;
        }
        else
        {
            uAck = (UACK)ROUTINE_ERROR_VOIDNESS_STATUS;
        }
        if (SessionPtr != nullptr)
        {
            MM_SAFE::Cpy(SessionPtr->GetSession(), sizeof(SESSION_ID), &sid, sizeof(SESSION_ID));
            SessionPtr->SetRet(ROUTINE_ERROR_VOIDNESS_SESSION);
            SessionPtr->SetStatus(SESSION_STATUS_VISA);
            SessionPtr->SetAddr(NetAddr);
            if (Stream.IsEnd() == false) // 业务数据
            {
                CBufReadStream& StreamData = SessionPtr->GetData();
                StreamData.Attach(Stream.Size() - Stream.Tell());
                Stream.Read(StreamData.GetBuf(), StreamData.Size());
            }
        }
    }
    else
    {
        uAck = (UACK)ROUTINE_ERROR_OVER_LIMIT;
    }
    ResetAck(pVisa, PAKE_AUTHOR_VISAACK, uAck);
    if (uAck == (UACK)ERRORD_OKAY)
    {
        ullParam += (ULLong)sid.llUpdate; // !!!!!!最简单的相加, 可以用外网端唯一性的数据进行加密/解密

        CNetAddr adrExternal = ((PGATE_ADDR)m_pServer->GetServerInfo(PAKT_GATE))->NetAddr[GATEI_EXTERNAL];

        CStream& StreamRet = pVisa->WriteStream(m_pServer->GetNetworkPtr());
        StreamRet << sid.llUpdate << ullParam << (UInt)m_uMasterId << (UInt)m_pServer->GetServerInfo(PAKT_GATE)->usId;
        adrExternal.Serialize(StreamRet);
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]授权连接[%p]业务区签证流程申请回复, 返回数据%llX---%llX!"), pVisa->GetSessionId(), sid.llUpdate, ullParam);
#endif

    }
#ifdef __DUMP_STEP_DETAIL__
    else
    {
        DEV_WARN(TF("[业务网关例程]授权连接[%p]业务区签证流程申请异常--->%d!"), ullParam, uAck);
    }
#endif
    pVisa->Addr(NetAddr);
    return pVisa->Send(m_pServer->GetSocket(PAKT_AUTHOR), m_pServer->GetNetworkPtr());
}

bool CGateRoutine::OnExternalBond(CEventBase& EventRef, ULLong ullParam)
{
    CNETSrvBond* pBond = static_cast<CNETSrvBond*>(&EventRef);

    LLong    llUpdate  = 0;
    ULLong   ullAuthor = 0;
    pBond->GetStream() >> llUpdate >> ullAuthor;
    ullAuthor -= llUpdate; // !!!!!!最简单的相加, 可以用外网端唯一性的数据进行加密/解密

#ifdef __DUMP_STEP_DETAIL__
    DEV_DUMP(TF("[业务网关例程]网关连接[%p]业务区签证流程连接开始, 读取验证1数据[%llX---%llX]!"), ullParam, llUpdate, ullAuthor);
#endif
    CGateSessionPtr SessionPtr;
    UACK uAck = CheckSessionTime(llUpdate, ullAuthor, SESSION_STATUS_VISA, SESSION_STATUS_CHECK, SessionPtr);
    if (uAck == (UACK)ERRORD_OKAY)
    {
        llUpdate = SessionPtr->GetUpdate();
        pBond->SetSessionId(ullAuthor);

        ullAuthor ^= ullParam; // !!!!!!最简单的异或, 可以用外网端唯一性的数据进行加密/解密
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]网关连接[%p]业务区签证流程连接验证1回复, 返回数据%llX---%llX!"), ullParam, llUpdate, ullAuthor);
#endif
        m_pServer->GetNetworkPtr()->SetAttr((Socket_t)ullParam, PAKT_EXTERNAL_READY);

        ResetAck(pBond, PAKE_AUTHOR_CHECKACK);
        CStream& Stream = pBond->WriteStream(m_pServer->GetNetworkPtr());
        Stream << llUpdate << ullAuthor;

        pBond->Addr(SessionPtr->GetAddr());
        return pBond->Send(m_pServer->GetSocket(PAKT_AUTHOR), m_pServer->GetNetworkPtr());
    }
#ifdef __DUMP_STEP_DETAIL__
    DEV_WARN(TF("[业务网关例程]网关连接[%p]业务区签证流程连接验证1失败, 关闭连接!"), ullParam);
#endif
    ResetAck(pBond, PAKE_SERVICE_BONDACK, uAck);
    pBond->Send((Socket_t)ullParam, m_pServer->GetNetworkPtr(), true);
    return false;
}

bool CGateRoutine::OnExternalLogin(CEventBase& EventRef, ULLong ullParam)
{
    CNETSrvLogin* pLogin = static_cast<CNETSrvLogin*>(&EventRef);

    LLong  llUpdate = 0;
    ULLong ullAuthor = 0;
    pLogin->GetStream() >> llUpdate >> ullAuthor;
    ullAuthor ^= ullParam; // !!!!!!最简单的异或, 可以用外网端唯一性的数据进行加密/解密

#ifdef __DUMP_STEP_DETAIL__
    DEV_DUMP(TF("[业务网关例程]网关连接[%p]业务区签证流程登陆开始, 读取验证2数据[%llX---%llX]!"), ullParam, llUpdate, ullAuthor);
#endif
    CGateSessionPtr SessionPtr;
    UACK uAck = CheckSessionTime(llUpdate, ullAuthor, SESSION_STATUS_CHECK, SESSION_STATUS_COMPLETE, SessionPtr);
    if (uAck == (UACK)ERRORD_OKAY)
    {
        llUpdate = SessionPtr->GetUpdate();
        pLogin->SetSessionId(ullAuthor);

        SessionPtr->SetUpdate(CPlatform::GetRunningTime());
        SessionPtr->SetRet(ROUTINE_ERROR_VOIDNESS_SESSION);
 
        RemoveQueueSession(ullAuthor);
        AddSession(ullParam, ullAuthor, SessionPtr);
#ifdef __DUMP_STEP_DETAIL__
        DEV_DUMP(TF("[业务网关例程]网关连接[%p]业务区签证流程连接验证2确认, 通知对应授权业务区例程确认, ATTR=%d!---%d"), ullParam, PAKT_EXTERNAL_MASTER + SessionPtr->GetType(), SessionPtr->GetAddr().usAttr);
#endif
        m_pServer->GetNetworkPtr()->SetAttr((Socket_t)ullParam, PAKT_EXTERNAL_MASTER + SessionPtr->GetType());
        m_pServer->GetServerInfo(PAKT_GATE)->IncrOnline();

        ResetAck(pLogin, PAKE_AUTHOR_COMPLETEACK);
        CStream& Stream = pLogin->WriteStream(m_pServer->GetNetworkPtr());
        Stream << llUpdate;

        pLogin->Addr(SessionPtr->GetAddr());
        pLogin->Send(m_pServer->GetSocket(PAKT_AUTHOR), m_pServer->GetNetworkPtr());

        CGateSession::ARY_ROUTINEID aRID;
        GetAllRoutineId(aRID);
        // 发送在线业务RID
        CNETSrvLoginAck Ack;
        Ack.SetSessionId(ullParam);
        Ack.SetSrvId((USRVID)aRID.GetSize());
        if (m_bRID2External && (aRID.GetSize() > 0))
        {
            CStream& StreamAck = Ack.Alloc(m_pServer->GetNetworkPtr());
            StreamAck.Write(aRID.GetData(), sizeof(ULLong) * aRID.GetSize());
            return Ack.Send((Socket_t)ullParam, m_pServer->GetNetworkPtr());
        }
        return m_pServer->GetNetworkPtr()->Send((Socket_t)ullParam, Ack);
    }
#ifdef __DUMP_STEP_DETAIL__
    DEV_WARN(TF("[业务网关例程]网关连接[%p]业务区签证流程连接验证2失败, 关闭连接!"), ullParam);
#endif
    CPAKSrvLoginAck Ack(uAck);
    Ack.SetSessionId(ullParam);
    m_pServer->GetNetworkPtr()->Send((Socket_t)ullParam, Ack, CNETTraits::SEND_CLOSE);
    return false;
}

bool CGateRoutine::OnRoutine(CEventBase& EventRef, ULLong ullParam)
{
    if (m_uMasterId != 0)
    {
        CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);

        if ((pRoutine->GetSessionId() == ullParam) && (m_pServer->GetNetworkPtr()->GetAttr((Socket_t)ullParam) >= PAKT_EXTERNAL_MASTER))
        {
            return OnRoutineRid(ullParam, pRoutine);
        }
        else
        {
            UACK uAck = (pRoutine->GetSessionId() == ullParam) ? (UACK)ROUTINE_ERROR_VOIDNESS_SESSION : (UACK)ROUTINE_ERROR_VOIDNESS_STATUS;
            CPAKSrvFaultAck Ack(uAck);
            Ack.SetSessionId(ullParam);
            m_pServer->GetNetworkPtr()->Send((Socket_t)ullParam, Ack, CNETTraits::SEND_CLOSE);
#ifdef __DUMP_STEP_DETAIL__
            DEV_WARN(TF("[业务网关例程]网关会话[%p]业务请求失败, 会话异常=%d!"), ullParam, uAck);
#endif
        }
    }
    else
    {
        CPAKSrvUnlinkAck Ack(ROUTINE_ERROR_REQ_MASTER);
        Ack.SetType(PAKT_GATE);
        m_pServer->GetNetworkPtr()->Send(ullParam, Ack);
#ifdef __DUMP_STEP_DETAIL__
        DEV_WARN(TF("[业务网关例程]网关会话[%p]业务请求失败, 和业务管理断开还未连接成功!"), ullParam);
#endif
    }
    return false;
}

bool CGateRoutine::OnRoutineRid(ULLong ullParam, CNETRoutine* pRoutine)
{
    if (CheckRoutineId(pRoutine->GetRoutineId()))
    {
        if (pRoutine->GetEvent() == PAKE_ROUTINE_LINK)
        {
            LinkSessionRId(ullParam, pRoutine);
        }
        pRoutine->SetSessionId(ullParam);
        pRoutine->SetType(PAKT_GATE);
        pRoutine->SetSrvId((USRVID)m_pServer->GetServerInfo(PAKT_GATE)->usId);
        pRoutine->ResetSend(m_pServer->GetSocket(PAKT_MASTER), m_pServer->GetNetworkPtr(), pRoutine->Length());
        return true;
    }
    else
    {
        CPAKSrvUnlinkAck Ack(ROUTINE_ERROR_REQ_SERVICE); // 对应业务不在线
        Ack.SetType(PAKT_GATE);
        Ack.SetSessionId(pRoutine->GetRoutineId());
        m_pServer->GetNetworkPtr()->Send(ullParam, Ack);
#ifdef __DUMP_STEP_DETAIL__
        DEV_WARN(TF("[业务网关例程]网关会话[%p]业务请求失败, 对应业务RID=%llX不在线!"), ullParam, pRoutine->GetRoutineId());
#endif
        return false;
    }
}

bool CGateRoutine::OnRoutineAck(CEventBase& EventRef, ULLong ullParam)
{
    CNETRoutine* pRoutine = static_cast<CNETRoutine*>(&EventRef);
    ullParam = pRoutine->GetSessionId();

    if (m_pServer->GetNetworkPtr()->GetAttr((Socket_t)ullParam) >= PAKT_EXTERNAL_MASTER)
    {
        if ((pRoutine->GetEvent() == PAKE_ROUTINE_LINKACK) && (pRoutine->GetAck() == (UACK)ERRORD_OKAY))
        {
            LinkAckSessionRId(ullParam, pRoutine->GetRoutineId());
        }
        pRoutine->SetType(PAKT_GATE);
        pRoutine->ResetSend((Socket_t)ullParam, m_pServer->GetNetworkPtr(), pRoutine->Length());
        return true;
    }
    // 连接已经断开
    return false;
}

bool CGateRoutine::CheckQueueSessionStatus(void)
{
    CSyncLockWaitScope scope(m_QueueLock);
    for (PINDEX index = m_QueueMap.GetFirstIndex(); index != nullptr; )
    {
        GATE_SESSION_MAP_PAIR* pPair = m_QueueMap.GetNext(index);
        if (pPair->m_V->TimeoutUpdate(m_uTimeoutMin))
        {
            m_QueueMap.RemoveAt(reinterpret_cast<PINDEX>(pPair));
        }
    }
    return true;
}

bool CGateRoutine::CheckConfirmSessionStatus(void)
{
    CSyncLockWaitScope scope(m_SessionLock);
    for (PINDEX index = m_ConfirmMap.GetFirstIndex(); index != nullptr; )
    {
        CONFIRM_MAP_PAIR* pPair = m_ConfirmMap.GetNext(index);
        if (CPlatform::GetRunningTime() >= pPair->m_V.llTimeout)
        {
#ifdef __DUMP_STEP_DETAIL__
            DEV_DUMP(TF("[业务网关例程]网关会话[%p]对应的授权连接关闭!"), pPair->m_K);
#endif
            m_ConfirmMap.RemoveAt(reinterpret_cast<PINDEX>(pPair));
        }
    }
    return true;
}


#if (__PLATFORM_TARGET__ == PLATFORM_TARGET_WINDOWS)
// Windows Realse版本MSVC编译器如果不强制调用静态常量, 静态常量会被优化掉
BOOL WINAPI DllMain(HMODULE hModule, ULong ulReason, void* lpReserved)
{
    UNREFERENCED_PARAMETER( lpReserved );

    if (ulReason == DLL_PROCESS_ATTACH)
    {
        ::DisableThreadLibraryCalls(hModule);
        DEV_DUMP(TF("$ Link %s.dll --- DLL_PROCESS_ATTACH"), CGateRoutine::ms_RTTI.GetName());
    }
    else if (ulReason == DLL_PROCESS_DETACH)
    {
        DEV_DUMP(TF("$ Unlink %s.dll --- DLL_PROCESS_DETACH"), CGateRoutine::ms_RTTI.GetName());
    }
    return TRUE;
}
#endif
